# -*- encoding:utf-8 -*-

from rest_framework import serializers
from .models import Order,OrderDetail
from django_redis import get_redis_connection
from datetime import datetime
from course.models import Course,CourseExpire
from django.db import transaction
from coupon.models import UserCoupon

class OrderModelSerializer(serializers.ModelSerializer):
    """
        订单的序列化器
        继承ModelSerializer，本身带有 添加 更新的操作
    """
    class Meta:
        model = Order
        fields = ["id","order_number","pay_type","credit","coupon"]
        # extra_kwargs 设置序列化用到这些字段还是反序列化用到这些字段

        extra_kwargs = {
            "id":{"read_only":True},        #id返回客户端才用到，所以read_only=True
            "order_number":{"read_only":True},
            "pay_type":{"write_only":True},  #pay_type支付方式是前端传给后端，所以是write_only=True
            "credit":{"write_only":True},  #credit使用积分数量多少是前端传给后端，所以是write_only=True
            "coupon":{"write_only":True},  #coupon使用优惠卷多少是前端传给后端，所以是write_only=True
        }


    #校验前端发送过来的值的数据
    def validate(self, attrs):
        """
        校验前端发送过来的值的数据
        attrs 是字典
        """

        pay_type = attrs.get("pay_type")
        try:
            Order.pay_choices[pay_type]
        except:
            raise serializers.ValidationError("对不起，当前不支持选中的支付方式！")

        # 判断积分使用是否上限
        user_credit = self.context["request"].user.credit
        credit = attrs.get("credit",0)
        if user_credit != 0 and user_credit < credit:
            raise serializers.ValidationError("当前使用积分超过上限")


        # 判断优惠卷是否在使用期间，是否是未使用状态

        user_coupon_id = attrs.get("coupon")     # 前端会传优惠卷id，积分，支付方式
        if user_coupon_id > 0:
            now = datetime.now()
            now_time = now.strftime("%Y-%m-%d %H:%M:%S")
            try:
                user_coupon = UserCoupon.objects.get(pk=user_coupon_id,start_time__lte=now_time,is_show=True,is_deleted=False,is_use=False)

            except:
                raise serializers.ValidationError("对不起，当前优惠卷不可用或者不存在")
            timer_timestamp = user_coupon.coupon.timer * 24 * 60 * 60
            start_timestamp = user_coupon.start_time.timestamp()
            end_timestamp = start_timestamp + timer_timestamp
            if end_timestamp < now.timestamp(): #过期

                raise serializers.ValidationError("对不起，当前优惠卷不可用或者不存在")


        # 一定要return验证结果
        return attrs


    def create(self, validated_data):
        """
            保存生成的订单
            先执行validate 进行字段校验返回attrs，再走到这个函数，数据都在validated_data中
            使用事物完成订单的生成，保证在生成订单的时候所有步骤都是成功的
        """

        """生成唯一订单号[结合时间+用户id+随机数(低增值<在redis中针对一个数值不断递增>)]"""
        redis_conn = get_redis_connection("cart")   #链接redis 3号库

        # 在序列化器中提取用户信息，序列化器第一个参数传模型对象用来序列化，第二个参数用来反序列化的数据，第三个参数context是视图views调用序列化器
        # 例如 OrderSerializer(instance="模型",data=data,context={"views":"视图对象","request":"请求对象"....})
        # postman测试的时候给1
        user_id = self.context["request"].user.id   #所以直接通过self.contest下拿request就行

        incr = int(redis_conn.incr("order"))       #通过redis不断获取递增值

        order_number = datetime.now().strftime("%Y%m%d%H%M%S") + "%06d" % user_id + "%06d" % incr

        # 使用mysql事物 在with语句内的全部按照事物来处理
        with transaction.atomic():
            #记录事物的回滚点，后面报错回滚的时候需要用到
            save_id = transaction.savepoint()

            """先生成订单"""
            order = Order.objects.create(
                order_title="luffy课程购买",
                total_price=0,
                real_price=0,
                order_number=order_number,  # 订单号
                order_status=0,
                pay_type=validated_data.get("pay_type"),
                credit=validated_data.get("pay_type", 0),
                coupon=validated_data.get("pay_type", 0),
                order_desc="",
                user_id=user_id

            )

            """然后再生成订单详情[记录本次下单所有商品信息]"""
            # 获取购物车中所有商品数据
            cart_bytes_dict = redis_conn.hgetall("cart_%s" % (user_id))
            selected_bytes_list = redis_conn.smembers("selected_%s" % (user_id))

            #开启redis事物
            pipe = redis_conn.pipeline()
            pipe.multi()


            for course_id_bytes, expire_id_bytes in cart_bytes_dict.items():
                course_id = int(course_id_bytes.decode())
                expire_id = int(expire_id_bytes.decode())
                if course_id_bytes in selected_bytes_list:
                    try:
                        course = Course.objects.get(is_show=True, is_deleted=False, pk=course_id)
                    except Course.DoesNotExist:
                        """这里报错需要回滚"""
                        # 回滚事物[消除save_id开始定义的时候到这一行之间，所有sql的语句产生的影响抹除]
                        transaction.savepoint_rollback(save_id)   #save_id是上面定义好的回滚点
                        raise serializers.ValidationError("对不起购买的商品不存在或者已下架")

                    # 判断课程有效期，获取课程原价
                    origin_price = course.price
                    try:
                        # 根据不同有效期，获取课程原价
                        if expire_id > 0:
                            # 查找到CourseExpire 表中
                            courseexpire = CourseExpire.objects.get(id=expire_id)
                            origin_price = courseexpire.price
                    except CourseExpire.DoesNotExist:
                        pass
                    # 默认真实价格是原价 如果没有参与活动，就代表是原价
                    real_price = course.real_price(expire_id)

                    try:
                        OrderDetail.objects.create(
                            order=order,
                            course=course,
                            expire=expire_id,
                            price=origin_price,
                            real_price=real_price,
                            discount_name=course.discount_name
                        )
                    except:
                        # 第2个回滚事物代码 [消除save_id开始定义的时候到这一行之间，所有sql的语句产生的影响抹除]
                        transaction.savepoint_rollback(save_id)  # save_id是上面定义好的回滚点
                        raise serializers.ValidationError("对不起，订单生成失败！")
                    """计算订单总价格，for循环一次次在数据库中更新字段下的值"""
                    order.total_price += float(real_price)  # 商品总价格


                    # 既然购买了，在购物车已经购买过的商品需要移除掉，上面已经开启了redis事物
                    pipe.hdel("cart_%s"%(user_id),course_id)    #删除哈希的值
                    pipe.srem("selected_%s"%(user_id),course_id)    #删除集合的值

            #默认最终实付价格是订单总价格
            real_price = order.total_price
            try:
                #对总价格加入优惠卷折扣，前端会传优惠卷id，积分，支付方式

                user_coupon_id = validated_data.get("coupon")
                if user_coupon_id > 0:
                    user_coupon = UserCoupon.objects.get(pk=user_coupon_id)
                    if user_coupon.coupon.condition > order.total_price:
                        """如果订单总金额 比 使用条件价格低，则报错"""
                        transaction.savepoint_rollback(save_id)  # save_id是上面定义好的回滚点
                        raise serializers.ValidationError("生成订单失败，当前优惠卷不可用 或 没达到使用优惠卷的条件")
                    sale_num = float(user_coupon.coupon.sale[1:])
                    if user_coupon.coupon.sale[0] == "*":
                        """折扣优惠"""
                        real_price = order.total_price * sale_num
                    else:
                        """减免优惠"""
                        print('*' * 20 + "000" + '*' * 20)
                        real_price = order.total_price - sale_num
                    order.coupon = user_coupon_id


                # 对总价格加入积分抵扣
                credit = validated_data.get("credit")
                from luffyapi.settings import constants
                if credit > 0:
                    # 判断积分是否超过订单 价格折扣比例
                    if credit > real_price * constants.CREDIT_MONEY:
                        transaction.savepoint_rollback(save_id)  # save_id是上面定义好的回滚点
                        raise serializers.ValidationError("生成订单失败，订单中使用的积分超过使用上限")
                    real_price = float("%.2f"%(real_price - credit/constants.CREDIT_MONEY))
                    order.credit = credit

                order.real_price = real_price
                #这两步对mysql和redis数据库的事物报错 也需要回滚
                order.save()
                pipe.execute()
            except:
                transaction.savepoint_rollback(save_id)
                raise serializers.ValidationError("对不起订单生成失败")
        # 返回生成的模型
        return order


























